package metamodel.coreservices;

import metamodel.*;
import metamodel.visitor.ObjectGraphIterator;
import metamodel.visitor.ObjectGraphVisitor;

import java.io.Writer;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;

/**
 * User: felix
 * Date: 23.06.2005
 */
public class Serializer implements ObjectGraphVisitor {
    ClassNode meta;
    Writer out;
    int index;
    StringMapper mapper=new DefaultStringMapper();
    private HashMap object2index;

    public Serializer(ClassNode meta, Writer out) {
        this.meta = meta;
        this.out = out;
    }

    public void serialize(Object data){
        index=0;
        object2index=new HashMap();
        ObjectGraphIterator iter=new ObjectGraphIterator(meta, this, ObjectGraphIterator.TraversalMode.NOTIFY_CYCLES);
        if (data instanceof Collection) {
            Collection collection = (Collection) data;
            iter.iterateCollection(collection);
        } else{
            iter.iterate(data);            
        }
    }


    public void visitRoot(ClassNode classNode, Object data) {
        object2index.put(data,new Integer(index));
        index++;
        try {
            out.write("(");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public void visitObject(ClassNode classNode, DeepProperty property, Object value, Object parent) {
        object2index.put(value,new Integer(index));
        index++;

        try {
            out.write("(");
            if (classNode.getDeclaringParent() instanceof PolyClassNode){
                out.write(value.getClass().getName());
                if (classNode.getProperties().size()>0){
                    out.write(",");
                }
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public void visitCollection(CollectionRef property, Object value, Object parent) {
        object2index.put(value,new Integer(index));
        index++;
        try {
            out.write("[");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public void visitFlatProperty(FlatProperty property, Object value, Object parent, boolean hasNext) {
        try {
            out.write("\""+mapper.toString(property,value)+"\"");
            if (hasNext)
              out.write(",");

        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }

    public void visitNull(Property property, Object parent, boolean hasNext) {
        try {
            out.write("null");
            if (hasNext)
              out.write(",");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public void visitCyclic(ClassNode classNode, DeepProperty property, Object value, Object parent, boolean hasNext) {
        int refIndex=((Integer)object2index.get(value)).intValue();
        try {
            out.write("#"+refIndex);
            if (hasNext)
                out.write(",");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }


    }

    public void endRoot(ClassNode classNode, Object data) {
        try {
            out.write(")");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public void endObject(ClassNode classNode, DeepProperty dp, Object data, Object parent, boolean hasNext) {
        try {
            out.write(")");
            if (hasNext)
              out.write(",");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public void endCollection(CollectionRef collRef, Collection data, Object parent, boolean hasNext) {
        try {
            out.write("]");
            if (hasNext)
              out.write(",");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public void setMapper(final StringMapper mapper) {
        this.mapper = mapper;
    }


}
